package com.hruiworks.campusgroup.service.impl;

import com.alibaba.fastjson2.JSON;
import com.hruiworks.campusgroup.common.SseBaseResponse;
import com.hruiworks.campusgroup.enums.SseMessageType;
import com.hruiworks.campusgroup.enums.SuccessCode;
import com.hruiworks.campusgroup.manager.entity.GroupFullInfo;
import com.hruiworks.campusgroup.pojo.entity.Activity;
import com.hruiworks.campusgroup.pojo.entity.Group;
import com.hruiworks.campusgroup.pojo.entity.User;
import com.hruiworks.campusgroup.quartz.common.QuartzGroupUtils;
import com.hruiworks.campusgroup.quartz.common.QuartzNameUtils;
import com.hruiworks.campusgroup.quartz.factory.job.GroupStartNoticeJobFactory;
import com.hruiworks.campusgroup.quartz.factory.trigger.GroupStartNoticeTriggerFactory;
import com.hruiworks.campusgroup.service.NoticeService;
import com.hruiworks.campusgroup.service.ScheduledJobService;
import com.hruiworks.campusgroup.service.SseService;
import com.hruiworks.campusgroup.utils.RedisUtils;
import com.hruiworks.usercheck.util.CronUtils;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.Arrays;
import java.util.List;

import static com.hruiworks.campusgroup.constants.redisKey.NoticeRedisKey.NOTICE_NEW_ACTIVITY_GLOBAL_COUNT;
import static com.hruiworks.campusgroup.constants.redisKey.NoticeRedisKey.NOTICE_NEW_ACTIVITY_USER;
import static com.hruiworks.campusgroup.constants.redisKey.UniversityRedisKey.UNIVERSITY_ALL;
import static com.hruiworks.campusgroup.enums.SseMessageType.GROUP_START_NOTICE;
import static com.hruiworks.campusgroup.enums.SseMessageType.NEW_ACTIVITY;

@Service
public class NoticeServiceImpl implements NoticeService {

    @Resource
    private ScheduledJobService scheduledJobService;

    @Resource
    private SseService sseService;

    @Override
    public void groupStartNoticeDefaultTime(User user, Group group, Activity activity) {
        groupStartNoticeCertainTime(user, group, activity, 30L, ChronoUnit.MINUTES);
    }

    @Override
    public void groupStartNoticeImmediately(User user, Group group, Activity activity) {
        GroupFullInfo groupFullInfo = new GroupFullInfo();
        groupFullInfo.setGroup(group);
        groupFullInfo.setActivity(activity);
        SseBaseResponse<GroupFullInfo> groupSseBaseResponse = new SseBaseResponse<>(groupFullInfo, SuccessCode.COMMON_SUCCESS, GROUP_START_NOTICE.getMessageType());
        String jsonString = JSON.toJSONString(groupSseBaseResponse);
        sseService.sendMessage(user, jsonString);
    }

    @Override
    public void groupStartNoticeCertainTime(User user, Group group, Activity activity, Long timeBeforehand, TemporalUnit unit) {
        GroupStartNoticeJobFactory groupStartNoticeJobFactory = new GroupStartNoticeJobFactory();
        JobDetail job = groupStartNoticeJobFactory.getJob(user, group, activity);
        GroupStartNoticeTriggerFactory triggerFactory = new GroupStartNoticeTriggerFactory();
        LocalDateTime startTime = group.getStartTime();
        LocalDateTime noticeTime = startTime.minus(timeBeforehand, unit);
        Trigger trigger = triggerFactory.getTrigger(user, group, noticeTime);
        scheduledJobService.registerJob(job, trigger);
    }

    @Override
    public void groupStartNoticeChangeTime(User user, Group group, Long timeBeforehand, TemporalUnit unit) {
        String triggerGroup = QuartzGroupUtils.groupStartNotice(user);
        String triggerName = QuartzNameUtils.groupStartNotice(group);
        LocalDateTime startTime = group.getStartTime();
        LocalDateTime noticeTime = startTime.minus(timeBeforehand, unit);
        scheduledJobService.changeJobTriggerTime(triggerName, triggerGroup, CronUtils.onlyOnce(noticeTime));
    }

    @Override
    @Async("otherTask")
    public void newActivityNoticeInstant(Activity activity) {
        SseBaseResponse<Activity> activitySseBaseResponse = new SseBaseResponse<>(activity, SuccessCode.COMMON_SUCCESS, NEW_ACTIVITY.getMessageType());
        String jsonString = JSON.toJSONString(activitySseBaseResponse);
        Integer schoolKey = (Integer) RedisUtils.hashGet(UNIVERSITY_ALL, activity.getUniversity());
        sseService.sendAllUserMessage(schoolKey, jsonString);
    }

    @Override
    public Integer userUnreadInitial(User user) {
        Integer schoolKey = (Integer) RedisUtils.hashGet(UNIVERSITY_ALL, user.getUniversity());
        // 获取总活动count
        Integer count = (Integer) RedisUtils.hashGet(NOTICE_NEW_ACTIVITY_GLOBAL_COUNT, schoolKey.toString());
        // 获取已读count
        if (RedisUtils.keyExists(NOTICE_NEW_ACTIVITY_USER+user.getId().toString())) {
            int intExact = Math.toIntExact(count - RedisUtils.bitCount(NOTICE_NEW_ACTIVITY_USER + user.getId()));
            return Math.max(intExact, 0);
        }
        return count;
    }

    @Override
    public Integer userRead(User user, String offsetStr) {
        if (!StringUtils.isBlank(offsetStr)) {
            // 已读标记
            String[] offsetArray = offsetStr.split(",");
            List<Long> offsetList = Arrays.stream(offsetArray).map(Long::valueOf).toList();
            RedisUtils.bitFieldSet(NOTICE_NEW_ACTIVITY_USER+user.getId(), offsetList, true);
        }
        // 返回已读count
        Integer schoolKey = (Integer) RedisUtils.hashGet(UNIVERSITY_ALL, user.getUniversity());
        Integer count = (Integer) RedisUtils.hashGet(NOTICE_NEW_ACTIVITY_GLOBAL_COUNT, schoolKey.toString());
        int intExact = Math.toIntExact(count - RedisUtils.bitCount(NOTICE_NEW_ACTIVITY_USER + user.getId()));
        return Math.max(intExact, 0);
    }

    @Override
    public void newMemberNotice(String university, List<Long> userIds, String message) {
        if (userIds.isEmpty()) {
            return;
        }
        SseBaseResponse<String> stringSseBaseResponse = new SseBaseResponse<>(message, SuccessCode.COMMON_SUCCESS, SseMessageType.NEW_MEMBER.getMessageType());
        String jsonString = JSON.toJSONString(stringSseBaseResponse);
        Integer schoolKey = (Integer) RedisUtils.hashGet(UNIVERSITY_ALL, university);
        sseService.sendGroupMessage(schoolKey, userIds, jsonString);
    }

}
